package wsi.match;

import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.rygh.core.html.Html;
import org.rygh.core.html.element.HtmlImg;
import org.rygh.core.html.element.HtmlParagraph;
import org.rygh.core.html.element.HtmlPre;
import org.rygh.core.html.element.HtmlSpan;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.model.common.MapSet;
import org.rygh.core.util.UIo;
import org.rygh.core.xml.XmlPath;
import org.rygh.semantic.gui.swing.input.Input;

import wsi.dataguide.tree.WSXmlLeaf;
import wsi.dataguide.tree.WSXmlObject;
import wsi.dataguide.tree.WSXmlTree;
import wsi.dataguide.tree.XmlObjectFunctionality;
import wsi.depend.assist.Assistant;
import wsi.depend.assist.AssistantManager;
import wsi.exp.ExpFolderController;
import wsi.function.DirManager;
import wsi.function.RuntimeInfo;
import wsi.script.AppConfig;
import wsi.script.AppXml;
import wsi.task.TaskRoutine;

public class FunctionalityXPath extends TaskRoutine {
	private final SubtreeController subtreeController;

	public FunctionalityXPath(DirManager dir) {
		this(dir, new SubtreeController(dir));
	}

	public FunctionalityXPath(DirManager dir,
			SubtreeController subtreeController) {
		super(dir);

		this.subtreeController = subtreeController;
	}

	private void assist() {
		if (!AppConfig.__DEBUG_APPEND_RERUN)
			return;

		RuntimeInfo runtime = dir.getFunction().getRuntimeInfo();

		if (!runtime.isAssistNeeded() || runtime.isAssistReady())
			return;

		AssistantManager am = AssistantManager.shared();
		Assistant a = am.assist(function);

		if (a == null)
			return;

		a.setSubtreeController(subtreeController);
		a.assist();
	}

	@Override
	public void compute() {
		// log.info("computing");
		// System.out.println("Data Guide :\n" + dataGuide.toString());

		subtreeController.computeIsomorphicRoot();

		List<WSXmlLeaf> dg_leaves = dataGuide.getAllLeaves();

		for (WSXmlLeaf leaf : dg_leaves) {
			WSXmlLeaf common = subtreeController.getCommonRoot();
			assert (common != null);

			if (leaf.isChildOf(common))
				computeSubleaf(leaf);
			else
				computeSupleaf(leaf);
		}

		assist();
	}

	private void computeSubleaf(WSXmlLeaf dg_leaf) {
		WSXmlObject obj = dg_leaf.getValue();

		MapSet<Input, String> map = obj.getValueMap();
		Set<String> valueSet = new HashSet<>();
		XmlObjectFunctionality func;

		for (Set<String> j : map.getMap().values())
			valueSet.addAll(j);

		int a, b, b2, c, d, n;

		a = valueSet.size();
		b = getDenominator(dg_leaf);
		assert a <= b : String.format("%d / %d, %s", a, b, dg_leaf.getXpath());

		func = obj.getInverseFunctionality();
		func.setNumerator(a);
		func.setDenominator(b);

		// System.out.printf("%s  IFunc: %s\n", leave.getXpath().getValue(),
		// func);

		c = getNumerator(dg_leaf);
		assert b >= c;

		func = obj.getFunctionality();
		func.setNumerator(c);
		func.setDenominator(b);

		// System.out.printf("%s   Func: %s\n", leave.getXpath().getValue(),
		// func);

		n = subtreeController.getSubrootCount();
		b2 = b + (n - c);
		assert b2 >= n;

		func = obj.getCompleteFunctionality();
		func.setNumerator(n);
		func.setDenominator(b2);

		// System.out.printf("%s C Func: %s\n", leave.getXpath().getValue(),
		// func);

		d = n - c > 0 ? 1 : 0;
		assert b2 >= a + d;

		func = obj.getCompleteInverseFunctionality();
		func.setNumerator(a + d);
		func.setDenominator(b2);

		// System.out.printf("%s CIFunc: %s\n", leave.getXpath().getValue(),
		// func);
	}

	/**
	 * 131014: cannot compute according to tree root only. should be according
	 * to isomorphic subtree root. functional w.r.t. subtree, not whole xml
	 * tree. should be w.r.t. isomorphic subtree root.
	 * 
	 * @param leaf
	 */
	private void computeSupleaf(WSXmlLeaf dg_leaf) {
		WSXmlObject obj = dg_leaf.getValue();

		MapSet<Input, String> map = obj.getValueMap();
		Set<String> valueSet = new HashSet<>();

		for (Set<String> j : map.getMap().values())
			valueSet.addAll(j);

		int a, b, b2, c, d, n;

		/*
		 * first obtain how many xml trees contain this leaf, then use the
		 * summation of subtree count of such xml trees as the denominator.
		 */

		a = valueSet.size();
		b = getDenominatorSup(dg_leaf);

		XmlObjectFunctionality func = obj.getInverseFunctionality();
		func.setNumerator(a);
		func.setDenominator(b);

		// System.out.printf("%s Sup  IFunc: %s\n",
		// dg_leaf.getXpath().getValue(),
		// func);

		c = getNumeratorSup(dg_leaf);

		func = obj.getFunctionality();
		func.setNumerator(c);
		func.setDenominator(b);

		// System.out.printf("%s Sup   Func: %s\n",
		// dg_leaf.getXpath().getValue(),
		// func);

		n = subtreeController.getSubrootCount();
		b2 = b + (n - c);

		func = obj.getCompleteFunctionality();
		func.setNumerator(n);
		func.setDenominator(b2);

		// System.out.printf("%s Sup C Func: %s\n",
		// dg_leaf.getXpath().getValue(),
		// func);

		d = n - c > 0 ? 1 : 0;

		func = obj.getCompleteInverseFunctionality();
		func.setNumerator(a + d);
		func.setDenominator(b2);

		// System.out.printf("%s Sup CIFunc: %s\n",
		// dg_leaf.getXpath().getValue(),
		// func);
	}

	private int countPairOccurrences(final WSXmlLeaf dg_leaf,
			final WSXmlLeaf xml_node) {
		List<WSXmlLeaf> leaves = xml_node.getAllLeaves();
		XmlPath dg_path = dg_leaf.getXpath();
		int count = 0;

		for (WSXmlLeaf leaf : leaves)
			if (dg_path.subsumesWithoutIndex(leaf.getXpath()))
				count++;

		return count;
	}

	/**
	 * 
	 * |{r,y:p(r,y)}| = |{y,r:p^-1(y,r)}|
	 * 
	 * @param dg_leaf
	 * @return
	 */
	private int getDenominator(WSXmlLeaf dg_leaf) {
		int all = 0;

		for (List<WSXmlLeaf> subroots : subtreeController.getAllSubroots()) {

			for (WSXmlLeaf root : subroots) {
				// System.out.println("getDenominator "
				// + root.getValue().getValue());

				int count = countPairOccurrences(dg_leaf, root);

				// System.out.printf("\tdenominator %d / %d for %s\n", count,
				// all,
				// root.getXpath());

				all += count;
			}
		}

		// System.out
		// .println("\ngetDenominator " + all + " " + dg_leaf.getXpath());

		return all;
	}

	/**
	 * 
	 * |{r,y:p(r,y)}| = |{y,r:p^-1(y,r)}|
	 * 
	 * @param dg_node
	 * @return
	 */
	private int getDenominatorSup(WSXmlLeaf dg_node) {
		Map<Input, WSXmlTree> rootMap = subtreeController.getXmlTreeMap();

		int all = 0;

		for (Entry<Input, WSXmlTree> e : rootMap.entrySet()) {
			WSXmlTree tree = e.getValue();

			List<WSXmlLeaf> subroots = subtreeController
					.getSubroots(e.getKey());

			if (subroots == null)
				continue;

			int count = countPairOccurrences(dg_node, tree.getConcreteRoot());

			// System.out.printf("\tdenominator %d for %s @ %s\n", count, node
			// .getObject().getName(), r.getXpath());

			all += count * subroots.size();
		}

		return all;
	}

	/**
	 * |{r:\exists y:p(r,y)}|
	 * 
	 * @param node
	 * 
	 * @return
	 */
	private int getNumerator(WSXmlLeaf node) {
		int all = 0;

		for (List<WSXmlLeaf> subroots : subtreeController.getAllSubroots()) {
			for (WSXmlLeaf r : subroots) {
				// System.out.println(r.getNotes());

				int count = countPairOccurrences(node, r);

				// System.out.printf("\tdenominator %d for %s @ %s\n", count,
				// node
				// .getObject().getName(), r.getXpath());

				if (count > 0)
					all++;
			}
		}

		return all;
	}

	/**
	 * |{r:\exists y:p(r,y)}|
	 * 
	 * @param node
	 * 
	 * @return
	 */
	private int getNumeratorSup(WSXmlLeaf node) {
		Map<Input, WSXmlTree> rootMap = subtreeController.getXmlTreeMap();

		int all = 0;

		for (Entry<Input, WSXmlTree> e : rootMap.entrySet()) {
			WSXmlTree tree = e.getValue();

			List<WSXmlLeaf> subroots = subtreeController
					.getSubroots(e.getKey());

			if (subroots == null)
				continue;

			int count = countPairOccurrences(node, tree.getConcreteRoot());

			// System.out.printf("\tdenominator %d for %s @ %s\n", count, node
			// .getObject().getName(), r.getXpath());

			if (count > 0)
				all += subroots.size();
		}

		return all;
	}

	private void print(WSXmlLeaf leaf, TableData data, String master_xpath) {
		String path = leaf.getXpath().stripIndex();

		if (master_xpath != null) {
			assert (path.startsWith(master_xpath)) : path + ","
					+ leaf.getXpath().getValue() + ", " + master_xpath;

			path = path.substring(master_xpath.length() + 1);
		}

		TableTuple tuple = new TableTuple();
		tuple.add(leaf.getValue().getName());
		tuple.add(String.format("<u>%s</u>", path));

		for (int j = 0; j < 4; j++) {
			tuple.add(leaf.getValue().getFunctionalities()[j].toString());
			tuple.add(String.format("%.5f", leaf.getValue()
					.getFunctionalities()[j].getFunctionality()));
		}

		data.add(tuple);
	}

	@Override
	public String save() {
		if (AppXml.__DEBUG) {
			UIo.saveFile(new File(dir.getDebugDir(),
					"_debug_functionality_xpath.html"), toString());
		}

		// to transfer in memory
		UIo.saveFile(new File(dir.getFunctionDir(),
				XmlFunctionalityController.DEFAULT_FILE), toMachineString());

		// save the selected trees result, for experiment I.

		String dg_xml = dataGuide.toString(0);

		UIo.saveFile(
				new File(ExpFolderController.shared().getFolderForExp1(),
						String.format("%s_dataguide_schema.xml", dir
								.getFunction().getFullName())), dg_xml);

		if (AppXml.__DEBUG) {
			// update with selected info
			UIo.saveFile(new File(dir.getFunctionDir(),
					DirManager.DATA_GUIDE_SCHEMA), dg_xml);
		}

		XmlPath root_xpath = subtreeController.getCommonRoot().getXpath();

		Html html = new Html("");
		html.addElement(new HtmlSpan("Isomorphic subtree root: "));
		html.addElement(new HtmlPre(root_xpath.stripIndex()));
		return html.toString();
	}

	public String toMachineString() {
		StringBuilder sb = new StringBuilder();

		WSXmlLeaf common = subtreeController.getCommonRoot();
		String common_xpath = common.getXpath().stripIndex();

		sb.append(common_xpath);
		sb.append("\r\n");

		List<WSXmlLeaf> list = dataGuide.getAllLeaves();

		for (WSXmlLeaf l : list) {
			String path = l.getXpath().stripIndex();

			sb.append(path);
			sb.append("\r\n");

			for (int j = 0; j < 4; j++)
				sb.append(String.format("%.5f\r\n", l.getValue()
						.getFunctionalities()[j].getFunctionality()));

		}

		return sb.toString();
	}

	@Override
	public String toString() {
		List<WSXmlLeaf> leaves = dataGuide.getAllLeaves();

		/**/

		WSXmlLeaf common = subtreeController.getCommonRoot();
		String common_xpath = common.getXpath().stripIndex();

		/**/

		Html html = new Html(function.getFullName());

		HtmlImg img = new HtmlImg(
				"../../../../Documents/SummaryAlgorithm/def_functionality.png");
		img.setHeight(200);
		html.addElement(img);
		html.newline();

		html.addElement(subtreeController.getCommonRootCounter().toHtmlTable());
		html.newline();

		List<WSXmlLeaf> nodes = dir.getDataGuide().getNodeWithMoreOccurrence();

		if (!nodes.isEmpty())
			html.addElement(new HtmlParagraph(String.format(
					"Node with maximal occurrences: %s", nodes.get(0)
							.getXpath())));

		html.addElement(new HtmlParagraph(String.format(
				"Node with maximal children: %s", dir.getDataGuide()
						.getNodeWithMaxChildren().getXpath().getValue())));

		HtmlTable table;

		TableData data = new TableData();
		TableTuple tuple;

		String[] cols = {
		/**/"Tag",
		/**/"XPath",
		/**/"Func.",
		/**/"<i>f<sub>T</sub></i> (<i>p</i>)",
		/**/"Inv. Func.",
		/**/"<i>f<sub>T</sub></i> <sup>-1</sup>(<i>p</i>)",
		/**/"Comp. Func.",
		/**/"<i>f<sub>T'</sub></i> (<i>p</i>)",
		/**/"Comp. Inv. Func.",
		/**/"<i>f<sub>T'</sub></i> <sup>-1</sup>(<i>p</i>)", };

		tuple = new TableTuple();
		for (String s : cols)
			tuple.add(s);

		data.add(tuple);

		for (WSXmlLeaf l : leaves) {
			if (!l.isChildOf(common))
				continue;

			print(l, data, common_xpath);
		}

		data.sort(data.columnSize() - 1);

		table = new HtmlTable(common_xpath, data, true);
		html.addElement(table);

		/**/

		data = new TableData();
		tuple = new TableTuple();
		for (String s : cols)
			tuple.add(s);

		data.add(tuple);

		for (WSXmlLeaf l : leaves) {
			if (l.isChildOf(common))
				continue;

			print(l, data, null);
		}

		data.sort(data.columnSize() - 1);

		table = new HtmlTable(common_xpath, data, true);
		html.addElement(table);

		return html.toString();
	}
}
